﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace Patterns.Tools
{
	public class EnumRange
	{
		private readonly object _low;
		private readonly object _high;
		private MethodInfo _compareMethod;
		private object _comparer;

		public EnumRange(Enum low, Enum high)
		{
			Debug.Assert(low.GetType() == high.GetType());

			_low = low;
			_high = high;

			BuildComparer();
		}

		public bool Matches(Enum value)
		{
			return Compare(_low, value) <= 0
			       && Compare(_high, value) >= 0;
		}

		public override bool Equals(object obj)
		{
			if (!(obj is EnumRange)) return false;
			return GetHashCode() == obj.GetHashCode();
		}

		public override int GetHashCode()
		{
			return Convert.ToInt32(_low) ^ Convert.ToInt32(_high);
		}

		private int Compare(object value1, object value2)
		{
			Debug.Assert(value1.GetType() == value2.GetType());
			return (int) _compareMethod.Invoke(_comparer, new[] { value1, value2 });
		}

		private void BuildComparer()
		{
			Type targetType = _low.GetType();
			Type typeTemplate = typeof (Comparer<>);
			Type comparerType = typeTemplate.MakeGenericType(targetType);
			PropertyInfo defaultProperty = comparerType.GetProperty("Default", BindingFlags.Public | BindingFlags.Static);
			_comparer = defaultProperty.GetValue(this, null);
			_compareMethod = _comparer.GetType().GetMethod("Compare", new[] { targetType, targetType });
		}

		public static EnumRange From(Enum low, Enum high = null)
		{
			high = high ?? low;
			return new EnumRange(low, high);
		}
	}
}